//////////////////////////////////////////////////////////////////////////////
//
// File:  buff.cc
//
// Description:
//   This is the implementation file for the buff class that manages a buffer.
//
// Status:  Completed
// 
//#include <iostream>

#include "buff.h"
#include "types.h"

string print_hex_form(u8 x) {
  string rv = "0x";

  if((x/16) == 10)
    rv += "A";
  else if((x/16) == 11)
    rv += "B";
  else if((x/16) == 12)
    rv += "C";
  else if((x/16) == 13)
    rv += "D";
  else if((x/16) == 14)
    rv += "E";
  else if((x/16) == 15)
    rv += "F";
  else
    rv += char(48 + (x/16));

  if((x%16) == 10)
    rv += "A";
  else if((x%16) == 11)
    rv += "B";
  else if((x%16) == 12)
    rv += "C";
  else if((x%16) == 13)
    rv += "D";
  else if((x%16) == 14)
    rv += "E";
  else if((x%16) == 15)
    rv += "F";
  else
    rv += char(48 + (x%16));
  
  rv += " ";
  return rv;
}

string print_hex_form(u32 x) {
  string rv = "";
  for(unsigned int i = 0; i < sizeof(u32); i++) {
    rv += print_hex_form( *(((u8*)&x) +i) );
  }
  return rv;
}



buff::buff() {
  data = NULL;
  sz = 0;
  startByte = (u8)0xFF;
  stripped_ = false;
}

buff::~buff() {
  // Deallocate mem.
  clear();
}

bool buff::add(const void* toAdd, int bytes) {
  if((sz == 0) && (data == NULL)) {
    // Nothing in the buffer right now, so allocate toAdd # of bytes for toAdd.
    data = new u8[bytes];
    memcpy(data, toAdd, bytes);
    // Update the size of the buffer.
    sz = bytes;
  }
  else {
    // Buffer has something in it.  Append toAdd to it data & update size.
    
    u8* oldData = data;
    data = new u8[sz + bytes];
    if(data == NULL) {
//       *debug << "ERROR:  Couldn't allocate mem for an addition to buff" << endl
// 	    << "        "   << __FILE__ << "(" << __LINE__ << ")" << endl;
      return false;
    }
    // Copy the oldData to the data arr.
    memcpy(data, oldData, sz);
    // Copy the toAdd data to the data arr.
    memcpy(data+sz, toAdd, bytes);
    // Update size;
    sz += bytes;
    // Deallocate oldData (formerlly data)
    delete [] oldData;
    oldData = NULL;
  }
  return true;
}

void buff::clear() {

  // Deallocate memory 
  if (data != NULL) {
    if(sz == 0) {
      // wtf?  Error & Quit.
//       *debug << "WARNING:  buff size was 0 but data ptr was not NULL" << endl
// 	     << "        "    << __FILE__ << "(" << __LINE__ << ")" << endl;
    }
    delete [] data;
    data = NULL;
  }
  sz = 0;
  packetID = 0;
  packetSz = 0;
}

short buff::size() {
  return sz;
}

void* buff::getPtr() {
  return data;
}
void buff::setPacketID(u8 newID) {
  packetID = newID;
}
u8 buff::getPacketID() {
  return packetID;
}
void buff::setPacketSz(u16 newSz) {
  packetSz = newSz;
}
u16 buff::getPacketSz() {
  return packetSz;
}
bool buff::package() {
  short currPos = 0;

  // Prepend 0xFF, packageID, packet size to the buffer.
  short futureSize = 1 + 1 + sizeof(short) + sz;
  // This is the side after the header has been added.
  u8* oldData = data;
  data = new u8[sz + 1 + 1 + sizeof(short)];
  if(data == NULL) {
//     *debug << "ERROR:  Couldn't allocate mem for a package buff" << endl
// 	   << "        "   << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  memcpy(data, &startByte, 1);
  currPos += 1;
  memcpy(data + currPos, &packetID, 1);
  currPos += 1;

  // We use futureSize here because the sz value will not have been updated
  //   untill after the memcpys.
  memcpy(data + currPos, &futureSize, sizeof(short));
  currPos += sizeof(short);
  
  // If oldData is NULL then sz == 0.  We don't want to do a memcpy then.
  if(oldData != NULL) {
    // Copy the oldData to the data arr.
    memcpy(data + currPos, oldData, sz);
    // Deallocate oldData (formerlly data)
    delete [] oldData;
    oldData = NULL;
  }
  // Update size;
  sz += currPos;

//   *debug << "DEBUG:  Package ID " << print_hex_form(packetID)
// 	 << " packaged with size " << sz << "." << endl;
  return true;
}

// strip the header info off and leave just the data.
// Header info includes:  

//  - 0xFF   Initial 1 byte.
//  - 0x__   Packet ID 1 byte.
//  - 0x____ Packet size 2 bytes.

bool buff::strip() {
  if(sz == 0 || data == NULL) {
//     *debug << "ERROR:  Attempt to strip off the header to a null or 0 sized "
// 	   << "buffer. " << endl;
    return false;
  }
  //  *debug << "DEBUG: sz == " << sz << endl;
  //  print_buffer();
  //  *debug << "DEBUG:  finished printing buffer" << endl;
  //  extract(startByte);
  //  *debug << "DEBUG:  extracted startByte" << endl;
  //  extract(packetID);
  //  *debug << "DEBUG:  extracted packetID" << endl;
  //  extract(packetSz);
  // Manually set the remaining packet size because we just extracted it.



//   *debug << "DEBUG:  extracted packetSz" << endl;

  if(!extract(startByte) || !extract(packetID) || !extract(packetSz)) {
//     *debug << "ERROR:  strip of header failed" << endl
// 	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
    return false;
  }
  
  // Update the packetSz to represent the packet after the strip.
  packetSz -= (sizeof(u8) + sizeof(u8) + sizeof(short));

  if(startByte != 0xFF) {
    // Bad news.  Either we're off, or bnet broke protocol.
//     *debug << "WARNING:  First byte in packet recv is not 0xFF. " << endl;
      return false;
  }
//   *debug << "DEBUG:  stripped.  dataupdate: "  << endl
// 	 << "  startByte: " << print_hex_form(startByte) << endl
// 	 << "  packetID:  " << print_hex_form(packetID) << endl
// 	 << "  size left  " << sz << endl
// 	 << "  packetSz   " << packetSz << endl;
  return true;
};


// Remove from the front of the buffer
bool buff::extract(u8 &stuff) {
  u8 newStuff;
  if(sz > sizeof(u8)) {
    u8* oldData = data;
    data = new u8[sz - sizeof(u8)];

    if(data == NULL) {
//       *debug << "ERROR:  Couldn't allocate mem for a buff extract(u8)" << endl
// 	    << "        "   << __FILE__ << "(" << __LINE__ << ")" << endl;
      return false;
    }

    // Copy it.
    memcpy(&newStuff, oldData, sizeof(u8));
    packetSz -= sizeof(u8);
    sz -= sizeof(u8);
    memcpy(data, oldData + sizeof(u8), sz);

    // Deallocate oldData (formerlly data)
    delete [] oldData;
    oldData = NULL;
  }
  else if (sz == sizeof(u8)) {
    // This is all that's in the buffer.
    memcpy(&newStuff, data, sizeof(u8));
    stuff = newStuff;
    clear();
  }
  else {
//     *debug << "ERROR:  Attempt to extract more bytes than the buffer contains" 
// 	   << endl;
    return false;
  }
  stuff = newStuff;
  //  *debug << "DEBUG: extract(u8):  sz == " << sz << endl;
  return true;
}
bool buff::extract(u16 &stuff) {
  u16 newStuff;
  if(sz > sizeof(u16)) {
    u8* oldData = data;
    data = new u8[sz - sizeof(u16)];
    if(data == NULL) {
//       *debug << "ERROR:  Couldn't allocate mem for a buff extract(u16)" << endl
// 	    << "        "   << __FILE__ << "(" << __LINE__ << ")" << endl;
      return false;
    }

    // Copy it.
    memcpy(&newStuff, oldData, sizeof(u16));
 
    packetSz -= sizeof(u16);
    sz -= sizeof(u16);

    memcpy(data, oldData + sizeof(u16), sz);

    // Deallocate oldData (formerlly data)
    delete [] oldData;
    oldData = NULL;
  }
  else if (sz == sizeof(u16)) {
    // This is all that's in the buffer.
    memcpy(&newStuff, data, sizeof(u16));
    stuff = newStuff;
    clear();
  }
  else {
//     *debug << "ERROR:  Attempt to extract more bytes than the buffer contains" 
// 	   << endl;
    return false;
  }
  stuff = newStuff;
  //  *debug << "DEBUG: extract(u16):  sz == " << sz << endl;
  return true;
}
bool buff::extract(u32 &stuff) {
  u32 newStuff;
  if(sz > sizeof(u32)) {
    u8* oldData = data;
    data = new u8[sz - sizeof(u32)];
    if(data == NULL) {
//       *debug << "ERROR:  Couldn't allocate mem for a buff extract(u32)" << endl
// 	    << "        "   << __FILE__ << "(" << __LINE__ << ")" << endl;
      return false;
    }

    // Copy it.
    memcpy(&newStuff, oldData, sizeof(u32));
    packetSz -= sizeof(u32);
    sz -= sizeof(u32);
    memcpy(data, oldData + sizeof(u32), sz);

    // Deallocate oldData (formerlly data)
    delete [] oldData;
    oldData = NULL;
  }
  else if (sz == sizeof(u32)) {
    // This is all that's in the buffer.
    memcpy(&newStuff, data, sizeof(u32));
    stuff = newStuff;
    clear();
  }
  else {
//     *debug << "ERROR:  Attempt to extract more bytes than the buffer contains" 
// 	   << endl;
    return false;
  }
  stuff = newStuff;
  //  *debug << "DEBUG: extract(u32):  sz == " << sz << endl;
  return true;
}

bool buff::extract(u64 &stuff) {
  u64 newStuff;
  if(sz > sizeof(u64)) {
    u8* oldData = data;
    data = new u8[sz - sizeof(u64)];
    if(data == NULL) {
//       *debug << "ERROR:  Couldn't allocate mem for a buff extract(u64)" << endl
// 	    << "        "   << __FILE__ << "(" << __LINE__ << ")" << endl;
      return false;
    }

    // Copy it.
    memcpy(&newStuff, oldData, sizeof(u64));
    packetSz -= sizeof(u64);
    sz -= sizeof(u64);
    memcpy(data, oldData + sizeof(u64), sz);

    // Deallocate oldData (formerlly data)
    delete [] oldData;
    oldData = NULL;
  }
  else if (sz == sizeof(u64)) {
    // This is all that's in the buffer.
    memcpy(&newStuff, data, sizeof(u64));
    stuff = newStuff;
    clear();
  }
  else {
//     *debug << "ERROR:  Attempt to extract more bytes than the buffer contains" 
// 	   << endl;
    return false;
  }
  stuff = newStuff;
  //  *debug << "DEBUG: extract(u64):  sz == " << sz << endl;
  return true;
}


// Precondition:  *stuff must be allcoated or have space ahead of time!!!!!!!!
//                SEGFAULT is certain otherwise!

bool buff::extract(char *stuff) {
  // I am doing this by strcpy-ing copying a string into *stuff.  

  // I suppose this is why programming in C style is bad.  
  // *stuff could point to an area of memory either not allocated 
  // or not have enough space.

  char extracted[1024];
  u8 aChar;
  int i;

  // Although inefficient, these repetitve extract calls will update
  //   sz and packetSz and fix the buffer for us.  Reuse of code, yay.

  if(sz == 0) {
//     *debug << "ERROR:  Tried to extract(char*) from an empty buffer." << endl
// 	   << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;

    return false;
  }
  for(i = 0; ((extract(aChar)) && (aChar != '\0')); i++) {
    // Copy the character over.
    memcpy((extracted+i), &aChar, sizeof(char));
    if(sz == 0) {
//       *debug << "ERROR:  Unable to extract(*char).  Buffer is empty and " 
// 	     << endl << "         no '\\0' was found." << endl
// 	     << "        " << __FILE__ << "(" << __LINE__ << ")" << endl;
      return false;
    }
  }
  // Copy over the null too.
  memcpy((extracted+i), &aChar, sizeof(char));
  
  // Copy extracted to the stuff that is getting returned.
  strcpy(stuff, extracted);
  //  *debug << "DEBUG: extract(char*):  sz == " << sz << endl;
  return true;
}

bool buff::extract(string &stuff) {
  char extracted[1024];
  if(!extract(extracted)) {
//     *debug << "ERROR:  extract(string) call to extract(char*) failed."
// 	   << endl << "          " << __FILE__ << "(" << __LINE__ << ")" 
// 	   << endl;
    return false;
  }
  stuff = (string)extracted;
  return true;
}



string buff::print_buffer() {
  string rv;
   for(int i=0; i < sz; i++) {
     rv += print_hex_form(*(data+i));
   }
   rv += string("\n");
   return rv;
}

void buff::setStripped( const bool& stripped )
{
    stripped_ = stripped;
}

bool buff::stripped() const
{
    return stripped_;
}
